using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using RNChessBoardCommonTypes;
using RNChessRulesEngine;
using Piece = System.Int32;

//---------------------------------------------------------------------------//
//																			 //
//			r  n  C  h  e  s  s  B  o  a  r  d  C  o  n  t  r  o  l			 //
//			=======================================================			 //
//						  (c) 2003 Chris R. Chapman							 //
//																			 //
//	Version:		1.0	FULL												 //
//  Date:			June 2003												 //
//  Author:			Chris R. Chapman (chris@chapmanconsulting.ca)			 //
//  Description:	A chessboard GUI written in 100% .Net/C#, featuring		 //
//					a resizable board with adjustable colors and configurable//
//					images for pieces, as well as events for capturing player//
//					movement and illegal moves.								 //
//																			 //
//					This version features a fully integrated rules engine    //
//					for verifying move legality and determining check and    //
//					mate game states.										 //
//																			 //
//																			 //
// LICENSING:		Can be freely distributed and used as long as the		 //
//					following criteria are met:								 //
//																			 //
//					1) All copyright notices are kept intact;				 //
//					2) Product is not for commercial resale;				 //
//					3) Product is NOT used in conjunction with ANY GPLed or  //
//					   FSF software.  I cannot abide by their philosophies.  //
//					4) Credit for the control to me is made in some			 //
//					   conspicuous fashion, like an About box.				 //
//---------------------------------------------------------------------------//
namespace RNChessBoard
{	
	// Control Event Delegate Forward Definitions
	public delegate void PlayerRetractMoveEventHandler(object sender, PlayerRetractMoveEventArgs e);
	public delegate void PlayerMoveEventHandler(object sender, PlayerMoveEventArgs e);
	public delegate void CurrentPlayerTurnEventHandler(object sender, CurrentPlayerTurnEventArgs e);
	public delegate void IllegalMoveEventHandler(object sender, IllegalMoveEventArgs e);
	public delegate void IllegalMoveAttemptEventHandler(object sender, IllegalMoveAttemptEventArgs e);
	public delegate void ResetChessBoardEventHandler(object sender);
	
	// Delegate forward declaration
	public delegate bool IsMoveLegit(string from, string to);

	/// <summary>
	/// The RNChessBoardControl.
	/// </summary>
	public class RNChessBoardControl : System.Windows.Forms.UserControl
	{
		#region Win32 API Declarations
		private const Int32 SND_SYNC = 0x0;
		private const Int32 SND_ASYNC = 0x1;
		private const Int32 SND_FILENAME = 0x20000;
		private const Int32 SND_RESOURCE = 0x40004;
		private const Int32 SND_MEMORY = 0x4;
		[DllImport("winmm")]
		public static extern bool PlaySound(string pszSound, Int32 hFile, UInt32 fdwSound);
		[DllImport("winmm")]
		public static extern bool PlaySound(string pszSound, string hFile, UInt32 fdwSound);
		[DllImport("winmm")]
		public static extern bool PlaySound(byte[] pByte, UInt32 param);
		#endregion

		#region Internal Datatypes
		/// <summary>
		/// Defines various types of game event sound effects.
		/// </summary>
		public enum SoundEffects
		{
			/// <summary>
			/// Sound played when a new game is started or a current game is reset.
			/// </summary>
			NEWGAME,
			/// <summary>
			/// Sound played when a player makes a non-capturing, legal move.
			/// </summary>
			MOVE,
			/// <summary>
			/// Sound played when a player makes a capturing, legal move.
			/// </summary>
			CAPTURE,
			/// <summary>
			/// Sound played when a player is put into check.
			/// </summary>
			CHECK,
			/// <summary>
			/// Sound played when a player is put into check mate.
			/// </summary>
			CHECKMATE,
			/// <summary>
			/// Sound played when a player makes an illegal move of any kind.
			/// </summary>
			ILLEGALMOVE
		}
		#endregion

		#region Rules Engine
		/// <summary>
		/// The Rules Engine
		/// </summary>
		private ChessRulesEngine _rulesEngine = new ChessRulesEngine();
		#endregion

        #region Event Declarations & Accessors
        private event IllegalMoveAttemptEventHandler _illegalMoveAttemptEvent = null;
        [Description("Event fired when a player attempts to make an illegal move, like touching an opponent's pieces.")]
        public event IllegalMoveAttemptEventHandler IllegalMoveAttempt
        {
            add { _illegalMoveAttemptEvent += value; }
            remove { _illegalMoveAttemptEvent -= value; }
        }

        private event PlayerRetractMoveEventHandler _playerRetractMoveEvent = null;
        [Description("Event fired when a player requests a move be retracted.")]
        public event PlayerRetractMoveEventHandler PlayerRetractMove
        {
            add { _playerRetractMoveEvent += value; }
            remove { _playerRetractMoveEvent -= value; }
        }

        private event PlayerMoveEventHandler _playerMovedEvent = null;
        [Description("Event fired when a player has just made a move.")]
        public event PlayerMoveEventHandler PlayerMove
        {
            add { _playerMovedEvent += value; }
            remove { _playerMovedEvent -= value; }
        }

        private event CurrentPlayerTurnEventHandler _currentPlayerTurnEvent = null;
        [Description("Event fired after a game turn has occurred.")]
        public event CurrentPlayerTurnEventHandler CurrentPlayerTurn
        {
            add { _currentPlayerTurnEvent += value; }
            remove { _currentPlayerTurnEvent -= value; }
        }

        private event IllegalMoveEventHandler _illegalMoveEvent = null;
        [Description("Event fired when the user attempts to make an illegal move on the board.")]
        public event IllegalMoveEventHandler IllegalMove
        {
            add { _illegalMoveEvent += value; }
            remove { _illegalMoveEvent -= value; }
        }

        private event ResetChessBoardEventHandler _resetChessBoardEvent = null;
        [Description("Event fired after the chessboard control has been reset.")]
        public event ResetChessBoardEventHandler ResetChessBoard
        {
            add { _resetChessBoardEvent += value; }
            remove { _resetChessBoardEvent -= value; }
        }
        #endregion

		#region Control Member Variables
		private System.ComponentModel.IContainer components;
		private bool _designTime = true;
		private int _frameWidth = 3;
		private int _squareSize;
		
		private Rectangle[,] _arrBoardGrid;
		private Hashtable _pieceBoxes = new Hashtable(64);
		private Color _darkSquareColor = Color.Tan;
		private Color _lightSquareColor = Color.Beige;
		private Rectangle _previousHighlightSquare = new Rectangle(0,0,0,0);
		private string _currentSquare = "";

		private System.Windows.Forms.ImageList ilChessPieces;
		private ChessPieceSet _blackPieces;
		private ChessPieceSet _whitePieces;
		
		private PictureBox _selectFromSquare = null;
		private PictureBox _selectToSquare = null;

		private bool _invertBoard = false;
		private bool _selectPiece = false;
		private bool _lockBoard = false;
		private bool _useChessRulesEngine = true;
		private bool _useInternalGameSounds = true;
		private bool _useAsRemoteClient = false;
		
		private PlayerTurn _clientPlayer = PlayerTurn.White;
		private PlayerTurn _playerTurn = PlayerTurn.White;

		private string _defaultPiecePositions = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 0";
		#endregion

		#region Property Accessors
		/// <summary>
		/// Get/Set the default board position and state using Forsyth-Edwards notation.
		/// </summary>
		/// <remarks>
		/// The setup position is verified using a FEN engine which will generate an exception if the notation
		/// contains errors and/or omissions.
		/// </remarks>
		[Description("Specify the default chessboard position and state using Forsyth-Edwards notation.")]
		public string DefaultPiecePositions
		{
			get { return this._defaultPiecePositions; }
			set
			{
				FENTranslator  fen = new FENTranslator (value);
				this._defaultPiecePositions = fen.ToString();
				if(_useChessRulesEngine)
				{
					_rulesEngine.ResetBoard();
					_rulesEngine.LoadFENPosition(_defaultPiecePositions);
					SetupChessboardFromArray(fen.ChessBoardArray);
				}
				else
				{
					this.ResetBoard(fen.ChessBoardArray);
				}
				_playerTurn = fen.ActiveColor;
				_clientPlayer = fen.ActiveColor;
			}
		}

		/// <summary>
		/// Get/Set whether to use the integrated chess rules engine to verify moves.
		/// </summary>
		[Description("Specify whether to use the integrated chess rules engine to verify moves.")]
		public bool UseChessRulesEngine
		{
			get { return _useChessRulesEngine; }
			set { _useChessRulesEngine = value; }
		}

		/// <summary>
		/// Get/set whether to use the internal game move sound effects.
		/// </summary>
		[Description("Specify whether to use the internal sound effects for various game events.")]
		public bool UseInternalGameSounds
		{
			get { return _useInternalGameSounds; }
			set { _useInternalGameSounds = value; }
		}
		
		/// <summary>
		/// Get/Set whether the chessboard control is being used in a peer-to-peer application.
		/// </summary>
		/// <remarks>
		/// If TRUE, the control will restrict access to the pieces when it is the remote client's
		/// turn to move, and permit access when it is the local player's turn.
		/// </remarks>
		[Description("Specify whether the chessboard control is being used in a peer-to-peer application.")]
		public bool UseAsRemoteClient
		{
			get { return _useAsRemoteClient; }
			set { _useAsRemoteClient = value; }
		}

		/// <summary>
		/// Get/Set whether the chessboard can be accessed, ie respond to click events.
		/// </summary>
		[Description("Get/set whether the chessboard GUI will respond to click events.")]
		public bool LockChessboardControl
		{
			get { return _lockBoard; }
			set { _lockBoard = value; }
		}

		/// <summary>
		/// Get current square algebraic coordinates
		/// </summary>
		public string CurrentSquareCoordinate
		{
			get { return _currentSquare; }
		}

		/// <summary>
		/// Get/set player turn status
		/// </summary>
		[Description("Get/set the local player turn, i.e. BLACK or WHITE side.")]
		public PlayerTurn PlayerTurnIndicator
		{
			get { return _playerTurn; }
			set { _playerTurn = value; }
		}

		/// <summary>
		/// Get/set which side the client is on
		/// </summary>
		[Description("Get/set the client player turn, i.e. BLACK or WHITE.  This is used for multiplayer remoting games.")]
		public PlayerTurn ClientPlayerIndicator
		{
			get { return _clientPlayer; }
			set { _clientPlayer = value; }
		}

		/// <summary>
		/// Get/Set the color for the dark squares on the board
		/// </summary>
		[Category("Appearance"),
		 Description("Set the color for the dark squares on the chessboard.")
		]
		public Color DarkSquareColor
		{
			get { return _darkSquareColor; }
			set {
				_darkSquareColor = value;
				this.Invalidate();
			}
		}

		/// <summary>
		/// Get/Set the color for the light squares on the board
		/// </summary>
		[Category("Appearance"),
		 Description("Set the color for the light squares on the chessboard.")
		]
		public Color LightSquareColor
		{
			get { return _lightSquareColor; }
			set {
				_lightSquareColor = value;
				this.Invalidate();
			}
		}
		
		/// <summary>
		/// Width of the chessboard frame in pixels.
		/// </summary>
		[Category("Appearance"),
		 Description("Get/Set the width of the chessboard frame in pixels.")]
		public int FrameWidth
		{
			set 
			{ 
				_frameWidth = value;
				FixBoardDimension();
				_squareSize = this.CalcSquareSize();
				this.Invalidate();
				DrawFrame();
			}
			get { return _frameWidth; }
		}

		[Category("Behaviour"),
		Description("The ImageList control to use for setting the chess piece graphics.")]
		public ImageList.ImageCollection ChessPieceSetImages
		{
			get { return this.ilChessPieces.Images; }
		}

		/// <summary>
		/// Size of the chessboard squares in pixels.
		/// </summary>
		public int SquareSize
		{
			get { return _squareSize; }
		}

		/// <summary>
		/// Returns the current list of moves made in standard algebraic notation.
		/// </summary>
		/// <remarks>
		/// The rules engine must be engaged for this feature, otherwise a blank string is returned.
		/// </remarks>
		public string SANMoveList
		{
			get 
			{ 
				if(_useChessRulesEngine)
				{
					return _rulesEngine.MovesList; 
				}
				else
				{
					return "";
				}
			}
		}
		#endregion

		#region Constructor & Setup Methods
		/// <summary>
		/// Chessboard constructor
		/// </summary>
		public RNChessBoardControl()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			SetupRNChessBoard();
		}		

		/// <summary>
		/// Setup routines
		/// </summary>
		private void SetupRNChessBoard()
		{
			// Create 8x8 array of Rectangle objects that will
			// represent the squares on the board.
			_arrBoardGrid = new Rectangle[8,8];
			
			// Build the data structures for the chessboard
			// and load up the pieces.
			BuildBoardHashtable();
			LoadPiecesFromImageList();

			// Make sure that the squares are "square" and set cursor.
			this.FixBoardDimension();
			this.Cursor = Cursors.Hand;

			// Load up the default position
			SetupChessboardFromFEN(this._defaultPiecePositions);

			// Wire up rules engine events
			_rulesEngine.PawnPromotion += new PawnPromotionEventHandler(this.rulesEngine_PawnPromotion);
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}
		#endregion

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(RNChessBoardControl));
			this.ilChessPieces = new System.Windows.Forms.ImageList(this.components);
			// 
			// ilChessPieces
			// 
			this.ilChessPieces.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
			this.ilChessPieces.ImageSize = new System.Drawing.Size(192, 32);
			this.ilChessPieces.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ilChessPieces.ImageStream")));
			this.ilChessPieces.TransparentColor = System.Drawing.Color.Red;
			// 
			// RNChessBoardControl
			// 
			this.Name = "RNChessBoardControl";
			this.Size = new System.Drawing.Size(192, 152);
			this.Resize += new System.EventHandler(this.RNChessBoardControl_Resize);
			this.SizeChanged += new System.EventHandler(this.RNChessBoardControl_SizeChanged);

		}
		#endregion
		
		#region Chessboard GUI Methods

		/// <summary>
		/// Overridden - paint form event to redraw the control
		/// </summary>
		/// <param name="pe"></param>
		protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe)
		{
			DrawFrame();
			DrawBoard();

			base.OnPaint(pe);

			//CurrentPlayerTurnEventArgs playerTurn = new CurrentPlayerTurnEventArgs(this._playerTurn);
			//OnCurrentPlayerTurn(playerTurn);
		}


		/// <summary>
		/// Overridden - method fired when control is first created on its site.
		/// We need to access this event in order to properly set the designtime/runtime flag.
		/// </summary>
		protected override void OnCreateControl()
		{
			ISite site = ((IComponent)this).Site;
			if(site!=null)
			{	
				this._designTime = site.DesignMode;		
			}
			else
			{
				this._designTime = false;
			}

			base.OnCreateControl();
		}

		/// <summary>
		/// Draws chessboard
		/// </summary>
		private void DrawBoard()
		{
			_squareSize = CalcSquareSize();
			bool squareDisp = false;		// f==white, t==black
			SolidBrush brBlack = new SolidBrush(_darkSquareColor);
			SolidBrush brWhite = new SolidBrush(_lightSquareColor);
			Graphics gObj = this.CreateGraphics();

			// Draw rows
			for(int r=0;r<8;r++)
			{
				int yCoord = _frameWidth + (_squareSize * r);
				
				// Draw columns
				for(int c=0;c<8;c++)
				{	
					int xCoord = _frameWidth + (_squareSize * c);
					if(squareDisp==true)		// black square
					{
						SituatePieceBox(r,c,xCoord,yCoord,_darkSquareColor);
					}
					else
					{
						SituatePieceBox(r,c,xCoord,yCoord,_lightSquareColor);
					}

					// Boolean switch to alternate colors across the board
					if(c<7)
					{
						squareDisp = !squareDisp;
					}
				}
			}
			
			// We're done with the graphics device context, so ditch it.
			gObj.Dispose();
		}

		/// <summary>
		/// Clears board of pieces
		/// </summary>
		private void WipeBoard()
		{
			IDictionaryEnumerator pieceBoxEnumerator = _pieceBoxes.GetEnumerator();
			while(pieceBoxEnumerator.MoveNext())
			{
				PictureBox pb = (PictureBox)pieceBoxEnumerator.Value;
				if(pb.Image != null)
				{
					pb.Image = null;
				}
			}
		}
		
		/// <summary>
		/// Locates a given picturebox object for holding chesspieces on the control
		/// </summary>
		/// <param name="row"></param>
		/// <param name="col"></param>
		/// <param name="xCoord"></param>
		/// <param name="yCoord"></param>
		private void SituatePieceBox(int row, int col, int xCoord, int yCoord, Color squareColor)
		{
			string key = ConvertCoordToNotation(row,col);
			if(_pieceBoxes.ContainsKey(key))
			{
				PictureBox pbTmp = (PictureBox)_pieceBoxes[key];
				pbTmp.Size = new System.Drawing.Size(_squareSize,_squareSize);
				pbTmp.Location = new System.Drawing.Point(xCoord,yCoord);
				pbTmp.BorderStyle = BorderStyle.None;
				pbTmp.BackColor = squareColor;
				this.Controls.Add(pbTmp);
			}
		}

		/// <summary>
		/// Draws the chessboard frame.
		/// </summary>
		private void DrawFrame()
		{
			int nFrameH;
			int nFrameW;

			Graphics gObj = this.CreateGraphics();
			nFrameH = nFrameW = this.Height - 1;
			Rectangle rBoardFrame = new Rectangle(0,0,nFrameW,nFrameH);
			Pen pn = new Pen(Color.Black,_frameWidth);
			gObj.DrawRectangle(pn,rBoardFrame);
			gObj.Dispose();
		}

		/// <summary>
		/// Squares-up the x and y dimensions of the chessboard
		/// and re-calculates constituent square sizing.
		/// </summary>
		private void DoResizeBoard()
		{
			this.Height = this.Width;
			_squareSize = CalcSquareSize();
			this.Invalidate();
		}

		/// <summary>
		/// Caclulates the dimension of the chessboard
		/// squares to fit the chessboard in an 8 x 8 grid.
		/// </summary>
		/// <returns>Integer dimension of the square</returns>
		private int CalcSquareSize()
		{
			return (this.Height - (_frameWidth * 2)) / 8;
		}

		/// <summary>
		/// Calculates the inner board scale modulus to
		/// determine if the scale will yield a board that
		/// can be evenly filled with 8 squares.
		/// </summary>
		/// <returns>Modulus remainder - 0 means perfect fit</returns>
		private int CalcInnerBoardScale()
		{
			return (this.Height - (_frameWidth * 2)) % 8;
		}

		/// <summary>
		/// Ensures board height and width are sufficient
		/// to accomodate the frame detail and eight evenly
		/// dimensioned squares across and down.
		/// </summary>
		private void FixBoardDimension()
		{
			int x = this.Height;
			int rem = 0;
			do
			{
				x++;
				rem = (x-(_frameWidth * 2)) % 8;
			} while(rem!=0);

			this.Height = this.Width = x;
		}
		#endregion

		#region Chesspiece Graphics Methods
		/// <summary>
		/// Set up a position on the chessboard for play using an integer array with standard piece
		/// definitions as defined in RNChessBoardCommonTypes.ChessPieceDefinition.
		/// </summary>
		/// <param name="chessboardArray">64-element array containing initial chessboard configuration</param>
		/// <remarks>The array is expected to have values corresponding to the constants
		/// as provided in the <see cref="ChessPieceDefinition"/>ChessPieceDefinition class.</remarks>
		public void SetupChessboardFromArray(int[] chessboardArray)
		{
			WipeBoard();
			DrawPiecesFromArray(chessboardArray);
			this._currentSquare="";
			this._selectFromSquare = null;
			this._selectToSquare = null;
			this._selectPiece = false;
		}

		/// <summary>
		/// Sets up a position on the chessboard for play using Forsyth-Edwards Notation.
		/// </summary>
		/// <param name="FENPositionString">Forsyth-Edwards Notation string detailing position and game state.</param>
		/// <remarks>
		/// Forsyth-Edwards Notation is comprised of six space delimited text fields:
		/// rnbqkbnr/p2p2Pp/2p5/1p3p2/R3PPP1/R7/PPP4P/1NBQK3 b KQkq - 0 2 
		/// 
		/// 1) Piece placement
		/// 2) Active side
		/// 3) Available castling (KQ denotes white king and queenside)
		/// 4) En passant target square
		/// 5) Half move counter -- used for the "50 move rule" -- not implemented here.
		/// 6) Full move counter
		/// </remarks>
		public void SetupChessboardFromFEN(string FENPositionString)
		{
			// If the rules engine is engaged, use it to parse the FEN string and 
			// build the position.
			if(_useChessRulesEngine)
			{
				WipeBoard();
				_rulesEngine.LoadFENPosition(FENPositionString);
				SetupChessboardFromArray(_rulesEngine.VirtualBoard);
				this.PlayerTurnIndicator = _rulesEngine.PlayerToMove;
				this.ClientPlayerIndicator = _rulesEngine.PlayerToMove;
			}
			else
			{
				WipeBoard();
				FENTranslator  fen = new FENTranslator (FENPositionString);
				SetupChessboardFromArray(fen.ChessBoardArray);
				this.PlayerTurnIndicator = fen.ActiveColor;
				this.ClientPlayerIndicator = fen.ActiveColor;
			}
		}

		/// <summary>
		/// Instantiates chess piece set objects and loads them from the control's ImageList
		/// </summary>
		/// <remarks>The chess set images for black and white pieces are contained within 32 x 192 bitmap images.
		/// Each piece is sliced from this set in 32 x 32 squares.
		/// </remarks>
		private void LoadPiecesFromImageList()
		{
			_blackPieces = new ChessPieceSet();
			_whitePieces = new ChessPieceSet();
            
			_blackPieces.LoadImages(ilChessPieces.Images[0]);
			_whitePieces.LoadImages(ilChessPieces.Images[1]);
		}

		/// <summary>
		/// Scans virtual board contents and renders them to the control... hopefully
		/// </summary>
		private void DrawPiecesFromArray(int[] chessBoardArray)
		{
			// TODO:  Clean up this logic - this is getting confusing.
			// I like accessing the squares by algebraic coordinates, but the bitboards
			// are indexed from 0-63, necessitating costly coversions -- I imagine that
			// the boxing/unboxing that's going on below can add up eventually
			PictureBox pbSquare = null;
			IEnumerator pbKeysEnum = _pieceBoxes.Keys.GetEnumerator();
			while(pbKeysEnum.MoveNext())
			{
				int squareIndex = this.ConvertNotationToCoord((string)pbKeysEnum.Current);
				int piece = chessBoardArray[squareIndex];

				pbSquare = (PictureBox)_pieceBoxes[(string)pbKeysEnum.Current];
				// Choose black or white chess set
				// Black pieces are just the negation of white piece values
				// ie: white pawn = 1, black pawn = -1
				if(piece < 0)	
				{			
					pbSquare.Image = _blackPieces.GetImageFromPieceType(piece);
				}
				else if(piece > 0)
				{
					pbSquare.Image = _whitePieces.GetImageFromPieceType(piece);
				}
			}
		}
		#endregion

		#region Chesspiece Movement Methods
		/// <summary>
		/// Moves selected piece to destination
		/// </summary>
		/// <returns>Boolean value indicating if the move was successful</returns>
		private bool MovePieceOnBoard()
		{
			// Raise notification event for clients
			PlayerMoveResult pmr = new PlayerMoveResult();
			
			// Do rules checking if required
			if(_useChessRulesEngine)
			{
				pmr = _rulesEngine.MakeMove(_selectFromSquare.Name,_selectToSquare.Name);
				if(_useInternalGameSounds) PlayPostMoveSound(ref pmr);
				if(pmr.PlayerMoveStatus==MoveResult.ILLEGAL)
				{
					OnIllegalMove(new IllegalMoveEventArgs("Illegal move.",pmr));
					return false;
				}
				// Handle special move circumstances
				switch(pmr.PlayerMoveType)
				{
					case MoveType.ENPASSANT:
						DoEnPassant();
						break;
					case MoveType.KINGSIDE_CASTLE:
						DoKingSideCastle();
						break;
					case MoveType.QUEENSIDE_CASTLE:
						DoQueenSideCastle();
						break;
					case MoveType.PROMOTION:
						CopyPiece(_selectFromSquare.Name,_selectToSquare.Name);
						DoPawnPromotion(pmr.PromotedPiece);
						break;
					case MoveType.CAPTURE: case MoveType.NONCAPTURE:
						CopyPiece(_selectFromSquare.Name,_selectToSquare.Name);
						break;
				}

			}
			else // Non-rules engine move handling;  reduced functionality
			{
				if(_selectToSquare.Image != null)
				{
					pmr.PlayerMoveType = MoveType.CAPTURE;
				}
				else
				{
					CheckForCastlingMove(ref pmr);
					if(pmr.PlayerMoveType!=MoveType.KINGSIDE_CASTLE && pmr.PlayerMoveType!=MoveType.QUEENSIDE_CASTLE)
					{
						CopyPiece(_selectFromSquare.Name,_selectToSquare.Name);
						pmr.PlayerMoveType = MoveType.NONCAPTURE;
					}
				}
				
			}
			
			// Raise PlayerMove event and toggle the local and client player sides.
			PlayerMoveEventArgs pme = new PlayerMoveEventArgs(pmr);
			OnPlayerMove(pme);
			if(!_useAsRemoteClient)	SwitchClientPlayerSide();
			SwitchLocalPlayerSide();
			return true;
		}

		/// <summary>
		/// Performs validation of a move against the rules engine, triggering
		/// sound effects based on the result.
		/// </summary>
		/// <param name="pmr"></param>
		private void PlayPostMoveSound(ref PlayerMoveResult pmr)
		{
			if(_useChessRulesEngine)
			{
				switch(pmr.PlayerMoveStatus)
				{
					case MoveResult.ILLEGAL:
						PlayGameEventSound(SoundEffects.ILLEGALMOVE,true);
						break;
					case MoveResult.LEGAL:
						PlayPieceMoveSound(pmr.PlayerMoveType);
						break;
					case MoveResult.CHECK:
						PlayPieceMoveSound(pmr.PlayerMoveType);
						PlayGameEventSound(SoundEffects.CHECK,true);
						break;
					case MoveResult.MATE:
						PlayPieceMoveSound(pmr.PlayerMoveType);
						PlayGameEventSound(SoundEffects.CHECKMATE,true);
						break;
				}
			}
		}

		/// <summary>
		/// Perform move according to coordinates
		/// This method will be used to display moves made by remote opponent, so
		/// we will assume that move legality has been done on their end.
		/// </summary>
		/// <param name="moveFrom"></param>
		/// <param name="moveTo"></param>
		public void MovePiece(string moveFrom, string moveTo)
		{
			// Set the input strings to proper caps and check for legal notation
			moveFrom = moveFrom.ToUpper();
			moveTo = moveTo.ToUpper();

			if(!CheckNotation(moveFrom) || !CheckNotation(moveTo))
			{
				throw new ArgumentException("Coordinates are not in standard algebraic notation:\n" + moveFrom + "," + moveTo);
			}

			// Set a reference to the PictureBox objects for the square the selected piece is moving from
			// and moving toward and then commit the move.
			_selectFromSquare = (PictureBox)_pieceBoxes[moveFrom];
			_selectToSquare = (PictureBox)_pieceBoxes[moveTo];
			MovePieceOnBoard();

			// Nullify selection squares so that mouse highlight displays correctly
			_selectFromSquare = null;
			_selectToSquare = null;
		}

		/// <summary>
		/// Removes a piece from the board.
		/// </summary>
		/// <param name="removeFrom">Algebraic coordinate for the chesspiece to remove.</param>
		public void RemovePiece(string removeFrom)
		{
			removeFrom = removeFrom.ToUpper();
			if(!CheckNotation(removeFrom))
			{
				throw new ArgumentException("Coordinate for removing piece is not in standard algebraic notation:\n" + removeFrom);
			}
			
			((PictureBox)_pieceBoxes[removeFrom]).Image = null;
		}

		/// <summary>
		/// Copies a piece from a source square to a target square.
		/// </summary>
		/// <param name="moveFrom"></param>
		/// <param name="moveTo"></param>
		private void CopyPiece(string moveFrom, string moveTo)
		{
			moveFrom = moveFrom.ToUpper();
			moveTo = moveTo.ToUpper();
			if(!CheckNotation(moveFrom) || !CheckNotation(moveTo))
			{
				throw new ArgumentException("Coordinates are not in standard algebraic notation:\n" + moveFrom + "," + moveTo);
			}

			_selectFromSquare = (PictureBox)_pieceBoxes[moveFrom];
			_selectToSquare = (PictureBox)_pieceBoxes[moveTo];

			_selectToSquare.Image = _selectFromSquare.Image;
			_selectFromSquare.Image = null;
		}

		/// <summary>
		/// Retracts the last move made, restoring the board to a given previous position.
		/// </summary>
		/// <param name="previousPosition">64-element array representing position of pieces on board.</param>
		public void RetractMove(int[] previousPosition)
		{
			// Reset the player turn and client player states.
			_playerTurn = _playerTurn==PlayerTurn.White ? PlayerTurn.Black : PlayerTurn.White;
			_clientPlayer = _clientPlayer==PlayerTurn.White ? PlayerTurn.Black : PlayerTurn.White;
			
			bool success = true;
			try
			{
				// Restore the board to the required position -- the moves should be valid as we're
				// relying on them coming from the virtual chessboard...
				SetupChessboardFromArray(previousPosition);
			}
			catch(Exception e)
			{
				success = false;	
			}
											
			// Raise signal event
			OnPlayerRetractMove(new PlayerRetractMoveEventArgs(success));
			
		}
		
		/// <summary>
		/// Retracts last move made, restoring board to a given previous position.
		/// </summary>
		/// <remarks>
		/// This method is only applicable if the chess rules engine is active, since
		/// it restores the previous cached position from the engine.
		/// </remarks>
		public void RetractMove()
		{
			if(_useChessRulesEngine)
			{
				if(_rulesEngine.UndoLastMove())
				{
					_playerTurn = _rulesEngine.PlayerToMove;
					_clientPlayer = _rulesEngine.PlayerToMove;
					SetupChessboardFromArray(_rulesEngine.VirtualBoard);
					OnPlayerRetractMove(new PlayerRetractMoveEventArgs(true));
				}
				else
				{
					OnPlayerRetractMove(new PlayerRetractMoveEventArgs(false));
				}
			}
		}

		/// <summary>
		/// Determines if the current move is consistent with one of the four
		/// castling patterns and performs the two-step piece movements as
		/// required.
		/// </summary>
		private void CheckForCastlingMove(ref PlayerMoveResult pmr)
		{
			// Check for castling -- this move would otherwise be validated by an external rules engine.
			if(_selectFromSquare.Image == _whitePieces.King)
			{
				if(_selectFromSquare.Name == "E1" && _selectToSquare.Name == "G1")
				{
					this.DoKingSideCastle();
					pmr.PlayerMoveType = MoveType.KINGSIDE_CASTLE;
				}
				else if(_selectFromSquare.Name == "E1" && _selectToSquare.Name == "C1")
				{
					this.DoQueenSideCastle();
					pmr.PlayerMoveType = MoveType.QUEENSIDE_CASTLE;
				}
			}
			else if(_selectFromSquare.Image == _blackPieces.King)
			{
				if(_selectFromSquare.Name == "E8" && _selectToSquare.Name == "G8")
				{
					this.DoKingSideCastle();
					pmr.PlayerMoveType = MoveType.KINGSIDE_CASTLE;
				}
				else if(_selectFromSquare.Name == "E8" && _selectToSquare.Name == "C8")
				{
					this.DoQueenSideCastle();
					pmr.PlayerMoveType = MoveType.QUEENSIDE_CASTLE;
				}	
			}	
		}

		/// <summary>
		/// Update chessboard to show a king-side castle move
		/// </summary>
		public void DoKingSideCastle()
		{
			// Performed after legal check
			if(this._playerTurn==PlayerTurn.White)
			{
				CopyPiece("E1","G1");
				CopyPiece("H1","F1");
			}
			else if(this._playerTurn==PlayerTurn.Black)
			{
				CopyPiece("E8","G8");
				CopyPiece("H8","F8");
			}
			//SwitchPlayerTurn();
		}

		/// <summary>
		/// Update chessboard to show a queen-side castle move
		/// </summary>
		public void DoQueenSideCastle()
		{
			// Performed after legal check
			if(this._playerTurn==PlayerTurn.White)
			{
				CopyPiece("E1","C1");
				CopyPiece("A1","D1");
			}
			else if(this._playerTurn==PlayerTurn.Black)
			{
				CopyPiece("E8","C8");
				CopyPiece("A8","D8");
			}

			//SwitchPlayerTurn();
		}
		
		/// <summary>
		/// Updates the chessboard GUI to reflect an en-passant pawn capture.
		/// </summary>
		public void DoEnPassant()
		{
			int epSquare=-1;
			if(_playerTurn==PlayerTurn.White)
			{
				epSquare = SquareIndexingTools.ConvertAlgebraicToIndex(_selectToSquare.Name) + 8;
			}
			else if(_playerTurn==PlayerTurn.Black)
			{
				epSquare = SquareIndexingTools.ConvertAlgebraicToIndex(_selectToSquare.Name) - 8;
			}
			
			if(epSquare!=-1)
			{
				RemovePiece(SquareIndexingTools.ConvertIndexToAlgebraic(epSquare));
			}

			CopyPiece(_selectFromSquare.Name,_selectToSquare.Name);
		}

		/// <summary>
		/// Updates the chessboard GUI to reflect a pawn promotion move.
		/// </summary>
		/// <param name="promotionPiece">Piece type for promoting the pawn into.</param>
		public void DoPawnPromotion(Piece promotionPiece)
		{
			// Rudimentary rules checking (sans rules engine)
			if(Math.Abs(promotionPiece) < ChessPieceDefinition.KNIGHT && Math.Abs(promotionPiece) > ChessPieceDefinition.QUEEN)
			{
				throw new ArgumentException("Invalid piece type for promotion -- must be KNIGHT, BISHOP, ROOK or QUEEN");
			}
			if((_playerTurn==PlayerTurn.Black && _selectToSquare.Image != _blackPieces.Pawn) ||
				(_playerTurn==PlayerTurn.White && _selectToSquare.Image != _whitePieces.Pawn))
			{
				throw new RNChessBoardControlException("Cannot promote a non-pawn piece or a pawn that is of the wrong color.");	
			}
			
			_selectToSquare.Image = (promotionPiece > 0 ? _whitePieces.GetImageFromPieceType(promotionPiece) :
				_blackPieces.GetImageFromPieceType(promotionPiece));
		}

		#endregion
		
		#region Chessboard Mechanics Methods

		/// <summary>
		/// Populates hashtable with PictureBox objects representing the squares on the chessboard
		/// </summary>
		/// <remarks>Note that the PictureBox.Name property for each square contains the algebraic
		/// coordinates for its location on the board.
		/// </remarks>
		private void BuildBoardHashtable()
		{
			for(int r=0 ; r < 8 ; r++)
			{
				for(int c=0; c < 8 ; c++)
				{
					string key = ConvertCoordToNotation(r,c);
					PictureBox pb = new PictureBox();
					pb.SizeMode = PictureBoxSizeMode.CenterImage;
					pb.Name = key;
					pb.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ChessboardSquare_MouseDown);
					pb.MouseEnter += new System.EventHandler(this.ChessboardSquare_MouseEnter);
					pb.MouseLeave += new System.EventHandler(this.ChessboardSquare_MouseLeave);

					_pieceBoxes.Add(key,pb);
					
				}
			}
		}

		
		/// <summary>
		/// Converts an algebraic notation string in the format [A-H][1-8] into an integer representing
		/// the location index (0-63) of the given coordinate.
		/// </summary>
		/// <param name="notationSquare"></param>
		/// <returns></returns>
		private int ConvertNotationToCoord(string algebraicCoord)
		{
			if(Regex.Match(algebraicCoord,"^[A-Ha-h][0-9]$").Success==false)
			{
				throw new ArgumentException("Notation string must be in the format [A-H][1-8].  Please verify your entry.");
			}

			int squareFile = (int)algebraicCoord[0] - 65;
			int squareRank = (8 - int.Parse(algebraicCoord[1].ToString())) * 8;
			
			return squareFile + squareRank;

		}


		/// <summary>
		/// Converts cartesian coordinate zero-based pair (x,y) to chess algebraic notation for use
		/// as a key to retrieve items from the pieceBoxes hashtable
		/// </summary>
		/// <param name="row"></param>
		/// <param name="col"></param>
		/// <returns></returns>
		private string ConvertCoordToNotation(int row, int col)
		{
			if(row > 7 || row < 0 || col > 7 || col < 0)
			{
				throw new ArgumentException("Row and column arguments must be zero-based and no greater than 7.");
			}
			
			int rConv;
			int cConv;
			if(_invertBoard==true)
			{
				rConv = row + 1;
				cConv = (7-col);
			}
			else
			{
				rConv = 8 - row;
				cConv = col;
			}

			string fileNotation = ((char)(65 + cConv)).ToString();

			return fileNotation + rConv.ToString();
		}

		/// <summary>
		/// Flips board and forces redraw
		/// </summary>
		public void InvertBoard()
		{
			this._invertBoard = !this._invertBoard;
			DrawFrame();
			DrawBoard();
		}

		/// <summary>
		/// Reset board to start game position from an array source.
		/// </summary>
		/// <remarks>
		/// This method resets the local and client player flags to WHITE.  Use the overloaded method to specifically
		/// set the client and local player flags.
		/// </remarks>
		/// <param name="initialPosition">64-element integer array representing the piece positions on the board.</param>
		public void ResetBoard(int[] initialPosition)
		{
			WipeBoard();
			if(_useChessRulesEngine)
			{	
				_rulesEngine.ResetBoard();
				_rulesEngine.VirtualBoard = initialPosition;
				_rulesEngine.PlayerToMove = PlayerTurn.White;
			}
			
			this.SetupChessboardFromArray(initialPosition);
			this._playerTurn = PlayerTurn.White;
			this._clientPlayer = PlayerTurn.White;
			this._selectToSquare = null;
			this._selectFromSquare = null;
			this._selectPiece = false;
		}

		/// <summary>
		/// Reset board to start game position from an array source and set the active player side.
		/// </summary>
		/// <param name="initialPosition">64-element integer array representing the piece positions on the board.</param>
		/// <param name="activePlayerColor">Player side color that is active.</param>
		public void ResetBoard(int[] initialPosition, PlayerTurn activePlayerColor)
		{
			if(_useChessRulesEngine)
			{
				// Since the base method does a lot of the setup,
				// we only need to set the active player side.
				ResetBoard(initialPosition);
				_rulesEngine.PlayerToMove = activePlayerColor;
			}
			else
			{
				ResetBoard(initialPosition);
				this._playerTurn = activePlayerColor;
				this._clientPlayer = activePlayerColor;
			}
		}

		/// <summary>
		/// Reset board to default position as specified by the DefaultPiecePositions property.
		/// </summary>
		public void ResetBoard()
		{
			if(_useChessRulesEngine)
			{
				WipeBoard();
				_rulesEngine.LoadFENPosition(this._defaultPiecePositions);
				SetupChessboardFromArray(_rulesEngine.VirtualBoard);
				this._playerTurn = _rulesEngine.PlayerToMove;
				this._clientPlayer = _rulesEngine.PlayerToMove;
				this._selectToSquare = null;
				this._selectFromSquare = null;
				this._selectPiece = false;
			}
			else
			{
				FENTranslator  fen = new FENTranslator (this._defaultPiecePositions);
				ResetBoard(fen.ChessBoardArray);
			}
		}

		/// <summary>
		/// Is player touching only their pieces?
		/// </summary>
		/// <returns></returns>
		private bool CanSelectPiece(Image chessPieceImage)
		{
			if(this._playerTurn==PlayerTurn.Black)
			{
				if(this._blackPieces.ChessPieceImageExists(chessPieceImage))
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			else if(this._playerTurn==PlayerTurn.White)
			{
				if(this._whitePieces.ChessPieceImageExists(chessPieceImage))
				{
					return true;
				}
				else
				{
					return false;
				}
			}

			return false;
		}


		/// <summary>
		/// Determine if player is allowed to make a move
		/// </summary>
		/// <returns></returns>
		private bool CanMove()
		{
			if(this._playerTurn==this._clientPlayer)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// Fire illegal move event
		/// </summary>
		/// <param name="description"></param>
		private void FireIllegalMoveAttempt(string description)
		{
			IllegalMoveAttemptEventArgs illMove = new IllegalMoveAttemptEventArgs(description);
			OnIllegalMoveAttempt(illMove);
			
			EraseHighlightFrame(_selectFromSquare);
			_selectFromSquare = null;
			_selectToSquare = null;
			_selectPiece = false;
			
		}

		/// <summary>
		/// Toggle the client player side color.
		/// </summary>
		public void SwitchClientPlayerSide()
		{
			if(this._clientPlayer == PlayerTurn.Black)
			{
				this._clientPlayer = PlayerTurn.White;
			}
			else
			{
				this._clientPlayer = PlayerTurn.Black;
			}
		}

		/// <summary>
		/// Toggle the local player side color.
		/// </summary>
		private void SwitchLocalPlayerSide()
		{
			if(this._playerTurn==PlayerTurn.Black)
			{
				this._playerTurn=PlayerTurn.White;
			}
			else
			{
				this._playerTurn=PlayerTurn.Black;
			}

			// Raise the CurrentPlayerTurn event to indicate that a move has been made,
			// confirmed and the player turn has been shifted.
			CurrentPlayerTurnEventArgs turnArgs = new CurrentPlayerTurnEventArgs(this._playerTurn);
			OnCurrentPlayerTurn(turnArgs);
		}
	
		/// <summary>
		/// Returns the current board state (chess piece positions) as a standard internal notation integer array.
		/// </summary>
		/// <returns>64 element integer array.</returns>
		/// <remarks>
		/// The array values correspond to the piece definitions as described in the ChessPieceDefinition class, with
		/// black pieces corresponding to the negation of white piece values.
		/// </remarks>
		public int[] GetBoardAsArray()
		{
			int[] boardArray = new int[64];
			IDictionaryEnumerator ide = _pieceBoxes.GetEnumerator();
			int sq = 0;
			while(ide.MoveNext())
			{
				PictureBox pb = (PictureBox)_pieceBoxes[ide.Key];
				Image pieceImage = pb.Image;
				int pieceVal = ChessPieceDefinition.NONE;
	
				if(pieceImage == _whitePieces.Pawn)
				{
					pieceVal = ChessPieceDefinition.PAWN;
				}
				else if(pieceImage == _whitePieces.Knight)
				{
					pieceVal = ChessPieceDefinition.KNIGHT;
				}
				else if(pieceImage == _whitePieces.Bishop)
				{
					pieceVal = ChessPieceDefinition.BISHOP;
				}
				else if(pieceImage == _whitePieces.Rook)
				{
					pieceVal = ChessPieceDefinition.ROOK;
				}
				else if(pieceImage == _whitePieces.Queen)
				{
					pieceVal = ChessPieceDefinition.QUEEN;
				}
				else if(pieceImage == _whitePieces.King)
				{
					pieceVal = ChessPieceDefinition.KING;
				}
				else if(pieceImage == _blackPieces.Pawn)
				{
					pieceVal = ChessPieceDefinition.PAWN * -1;
				}
				else if(pieceImage == _blackPieces.Knight)
				{
					pieceVal = ChessPieceDefinition.KNIGHT * -1;
				}
				else if(pieceImage == _blackPieces.Bishop)
				{
					pieceVal = ChessPieceDefinition.BISHOP * -1;
				}
				else if(pieceImage == _blackPieces.Rook)
				{
					pieceVal = ChessPieceDefinition.ROOK * -1;
				}
				else if(pieceImage == _blackPieces.Queen)
				{
					pieceVal = ChessPieceDefinition.QUEEN * -1;
				}
				else if(pieceImage == _blackPieces.King)
				{
					pieceVal = ChessPieceDefinition.KING * -1;
				}
				else
				{
					pieceVal = ChessPieceDefinition.NONE;
				}
				
				boardArray[ConvertNotationToCoord((string)ide.Key)] = pieceVal;
				sq++;
			}
			
			return boardArray;
		}

		/// <summary>
		/// Verifies the compliance of a square coordinate to standard algebraic notation (ie A8, H1, C3, etc.)
		/// </summary>
		/// <param name="squareCoordinate">Square coordinate to be evaluated</param>
		/// <returns>TRUE if coordinate complies with standard algebraic notation</returns>
		private bool CheckNotation(string squareCoordinate)
		{
			squareCoordinate = squareCoordinate.ToUpper();
			return Regex.Match(squareCoordinate,"^[A-H][1-8]$").Success;
		}

		#endregion

		#region Control Event Raising Methods
		/// <summary>
		/// Raises RNChessBoard.PlayerMove event in response indicating a player has finished their turn.
		/// </summary>
		/// <param name="e"></param>
		protected void OnPlayerMove(PlayerMoveEventArgs e)
		{
			if(_playerMovedEvent != null)
			{
				_playerMovedEvent(this, e);
			}
		}
		
		/// <summary>
		/// Raises RNChessBoard.PlayerRetractMove event indicating a player is retracting their last move.
		/// </summary>
		protected void OnPlayerRetractMove(PlayerRetractMoveEventArgs e)
		{
			if(_playerRetractMoveEvent != null)
			{
				_playerRetractMoveEvent(this,e);
			}
		}

		/// <summary>
		/// Raises RNChessBoard.PlayerTurn event indicating that a game turn has occurred and notifies
		/// subscribers which side is to play.
		/// </summary>
		/// <param name="e"></param>
		protected void OnCurrentPlayerTurn(CurrentPlayerTurnEventArgs e)
		{
			if(_currentPlayerTurnEvent != null)
			{
				_currentPlayerTurnEvent(this,e);
			}
		}
		
		/// <summary>
		/// Raises RNChessBoard.IllegalMove event indicating that the player has made an illegal move.
		/// </summary>
		protected void OnIllegalMove(IllegalMoveEventArgs e)
		{
			if(_illegalMoveEvent != null)
			{
				_illegalMoveEvent(this,e);
			}
		}

		/// <summary>
		/// Raises RNChessBoard.IllegalMoveAttempt event indicating that the player was attempting
		/// to make an illegal move such as touching their opponent's pieces or making a move when
		/// it is not their turn.
		/// </summary>
		/// <param name="e"></param>
		protected void OnIllegalMoveAttempt(IllegalMoveAttemptEventArgs e)
		{
			if(_illegalMoveAttemptEvent != null)
			{
				_illegalMoveAttemptEvent(this,e);
			}
		}

		/// <summary>
		/// Raises RNChessBoard.ResetChessBoard event indicating that the chessboard has been
		/// reset.
		/// </summary>
		protected void OnResetChessBoard()
		{
			if(_resetChessBoardEvent != null)
			{
				_resetChessBoardEvent(this);
			}
		}
		#endregion

		#region Control Event Handling Methods

		/// <summary>
		/// Event listener fired after board size changed.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void RNChessBoardControl_SizeChanged(object sender, System.EventArgs e)
		{
			if(CalcInnerBoardScale() != 0)
			{
				FixBoardDimension();
				DrawFrame();
			}
		}

		/// <summary>
		/// User resizes the chessboard
		/// - there are some strange issues here with squares getting larger everytime
		/// the board is expanded and/or contracted
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void RNChessBoardControl_Resize(object sender, System.EventArgs e)
		{
			DoResizeBoard();
		}

		/// <summary>
		/// User clicks down on any of the squares on the chessboard - custom event handler
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ChessboardSquare_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			// Check if board has been locked out
			if(_lockBoard == true)
			{
				return;
			}

			// Is the player allowed to move-- ie, not the client's turn
			if(!CanMove())
			{
				FireIllegalMoveAttempt("It is not your turn to move.");
				return;
			}

			// Determine piece and set up selection
			// Place logic to determine whose move it is
			PictureBox pbSquare = (PictureBox)sender;

			// Select chess piece to move
			if(_selectPiece==false && pbSquare.Image!=null)				// no selected piece and mouse over a piece
			{

				if(!CanSelectPiece(pbSquare.Image))
				{
					FireIllegalMoveAttempt("You cannot move your opponent's pieces!");
					return;
				}

				_selectPiece=true;
				DrawHighlightFrame(pbSquare,Color.Blue);
				_selectFromSquare = pbSquare;
				return;
			}
			else if(_selectPiece==true && pbSquare==_selectFromSquare)	// selected piece and mouse over selected piece
			{
				_selectPiece = false;
				EraseHighlightFrame(pbSquare);
				_selectFromSquare = null;
				return;
			}
			else if(_selectPiece==true && e.Button==MouseButtons.Right) // selected piece and right click
			{
				_selectPiece = false;
				EraseHighlightFrame(_selectFromSquare);
				_selectFromSquare=null;
			}
			
			// Select destination for piece move
			if(_selectPiece==true)
			{
				_selectToSquare = pbSquare;
				
				if(!MovePieceOnBoard())
				{
					return;
				}

				//SwitchPlayerTurn();
				_selectToSquare = null;
				_selectFromSquare = null;
				_selectPiece = false;

				// DEBUG - change sides for local client to test piece movement
				//SwitchSides();
			}

		}


		/// <summary>
		/// User moves mouse into a chessboard square
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ChessboardSquare_MouseEnter(object sender, System.EventArgs e)
		{
			PictureBox pbSquare = (PictureBox)sender;
			string squareLocation = pbSquare.Name;

			// Don't color over a highlighted selection square
			if(pbSquare==_selectFromSquare) return;

			// Draw highlight frame around current square
			Graphics hdc = pbSquare.CreateGraphics();
			Rectangle highlightRect = pbSquare.ClientRectangle;

			Pen highlightPen = new Pen(Color.Red,2);
            hdc.DrawRectangle(highlightPen,highlightRect);

			_previousHighlightSquare = highlightRect;
			hdc.Dispose();

			this._currentSquare = pbSquare.Name;
		}

		
		/// <summary>
		/// User moves mouse out of a chessboard square
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ChessboardSquare_MouseLeave(object sender, System.EventArgs e)
		{
			if(_selectPiece==false)
			{
				EraseHighlightFrame((PictureBox)sender);
			}
			else
			{
				if(_selectFromSquare!=(PictureBox)sender)
				{
					EraseHighlightFrame((PictureBox)sender);
				}
			}
		}
		
		/// <summary>
		/// Event handler triggered when the rules engine determines that the local player
		/// has moved a pawn into the 8th rank requiring a mandatory piece promotion.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void rulesEngine_PawnPromotion(object sender, PawnPromotionEventArgs e)
		{
			frmPawnPromotion pawnPromoteForm = new frmPawnPromotion(this.ilChessPieces);
			pawnPromoteForm.ShowDialog(this, e.PromotionSide);
			e.PromotedPiece = pawnPromoteForm.PromotedPiece;
		}
		#endregion

		#region Graphics - Square Highlighting Methods
		/// <summary>
		/// Draws highlight frame on a given chessboard PictureBox element
		/// </summary>
		/// <param name="currentSquare"></param>
		/// <param name="highlightColor"></param>
		private void DrawHighlightFrame(PictureBox currentSquare, Color highlightColor)
		{
			Graphics hdc = currentSquare.CreateGraphics();
			Rectangle selectHighlightRect = currentSquare.ClientRectangle;
			Pen selectHighlightPen = new Pen(highlightColor,2);
			hdc.DrawRectangle(selectHighlightPen,selectHighlightRect);
			hdc.Dispose();	
		}
		
		/// <summary>
		/// Removes highlight frame from a given chessboard PictureBox element
		/// </summary>
		/// <param name="currentSquare"></param>
		private void EraseHighlightFrame(PictureBox currentSquare)
		{
			if(currentSquare==null)
				return;

			Graphics hdc = currentSquare.CreateGraphics();
			Pen eraserPen = new Pen(currentSquare.BackColor,2);
			hdc.DrawRectangle(eraserPen,_previousHighlightSquare);
			hdc.Dispose();	
		}
		#endregion
		
		#region Sound Effects Methods
		/// <summary>
		/// Plays a specified sound.
		/// </summary>
		/// <param name="soundToPlay">Type of sound to play.</param>
		/// <param name="soundFlag">TRUE to play sound asynchronously, FALSE synchronously</param>
		public void PlayGameEventSound(SoundEffects soundToPlay, bool playAsync)
		{
			PlaySound(null,null,SND_ASYNC);
			UInt32 PLAYBACK = (UInt32)(playAsync==true ? SND_ASYNC | SND_MEMORY : SND_SYNC | SND_MEMORY);
			
			try
			{
				switch(soundToPlay)
				{
					case SoundEffects.CAPTURE:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.CAPTURE.WAV"),PLAYBACK);
						break;

					case SoundEffects.ILLEGALMOVE:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.illegalmove.wav"),PLAYBACK);
						break;

					case SoundEffects.MOVE:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.MOVE.WAV"),PLAYBACK);
						break;
				
					case SoundEffects.NEWGAME:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.NEWGAME.WAV"),PLAYBACK);
						break;

					case SoundEffects.CHECK:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.say_check.wav"),PLAYBACK);
						break;

					case SoundEffects.CHECKMATE:
						PlaySound(GetResourceBytes("RNChessBoard.SoundEffects.say_checkmate.wav"),PLAYBACK);
						break;

					default:
						return;
				}
			}
			catch(Exception e)
			{
				// Can't play the sound, so avoid it altogether
				return;
			}
		}

		/// <summary>
		/// Plays piece movement sounds according to the type of move made.
		/// </summary>
		/// <param name="playerMoveType">Type of move player has made.</param>
		private void PlayPieceMoveSound(MoveType playerMoveType)
		{
			if(playerMoveType==MoveType.NONCAPTURE || playerMoveType==MoveType.PROMOTION)
			{
				PlayGameEventSound(SoundEffects.MOVE,false);
			}	

			if(playerMoveType==MoveType.KINGSIDE_CASTLE || playerMoveType==MoveType.QUEENSIDE_CASTLE ||
				playerMoveType==MoveType.CAPTURE || playerMoveType==MoveType.ENPASSANT)
			{
				PlayGameEventSound(SoundEffects.CAPTURE,false);
			}
		}

		/// <summary>
		/// Returns a byte array of a provided resource.
		/// </summary>
		/// <param name="resourceFilePath">Fully qualified path in the assembly manifest for the resource.</param>
		/// <returns>Byte array for the content of the resource.</returns>
		private byte[] GetResourceBytes(string resourceFilePath)
		{
			Assembly execAssy = Assembly.GetExecutingAssembly();
			Stream resStream;
			byte[] resBytes;

			try
			{
				resStream = execAssy.GetManifestResourceStream(resourceFilePath);
				resBytes = new Byte[resStream.Length];
				resStream.Read(resBytes,0,(int)resStream.Length);
			}
			catch(Exception e)
			{
				// Problem-- could not read the bytes from the resource-- throw the error
				// up the stack.
				throw e;
			}

			return resBytes;
		}
		#endregion

		#region Miscellaneous Methods and Functions
		/// <summary>
		/// Returns the current chessboard position in Forsyth-Edwards Notation.
		/// </summary>
		/// <returns>String representing the current chessboard position in FEN.</returns>
		public string GetCurrentPositionAsFEN()
		{
			if(_useChessRulesEngine)
			{
				return _rulesEngine.GetFENPosition();
			}
			else
			{
				// Create a FENAnalyzer object to parse the current board configuration.
				// Since we can't know anything else beyond the state of the un-assisted board
				// besides the current player turn, we'll use the default FEN state properties.
				FENTranslator  fen = new FENTranslator (this.GetBoardAsArray());
				fen.ActiveColor = _playerTurn;
				return fen.ToString();
			}
		}
		#endregion
	}
}
